﻿#ifndef PST_WIDGETCUTTER_H
#define PST_WIDGETCUTTER_H

#include <vtkSmartPointer.h>
#include <vtkCommand.h>
#include <vtkPlaneWidget.h>
#include <vtkBoxWidget.h>

#include <vector>
#include <QString>
#include <array>

class vtkDataArray;
class vtkDoubleArray;
class vtkCutter;
class vtkActor;
class vtkPlane;
class vtkPlanes;
class vtkDataSet;
class vtkPolyDataMapper;
class vtkProperty;
class vtkLookupTable;
class vtkScalarBarActor;
class vtkRenderWindowInteractor;
class vtkScalarBarWidget;

/**
 * @class PlaneCutter
 * @brief 设置一个cutter来切割标量场(只显示切面数据,不更改源数据)。
 * 目前包括box和plane两种widget。按i键可以激活/取消激活widget。
 * 默认激活plane. 目前一个类只能实现一个切片，即plane和box都生成同一个actor.
 */

namespace pst
{

    class vtkWidgetCall : public vtkCommand
    {
    public:
        static vtkWidgetCall* New()
        {
            return new vtkWidgetCall;
        }
    public:

        enum class CallbackWidgetType
        {
            BOX,
            PLANE,
        };

    public:
        void SetCallbackWidgetType(CallbackWidgetType type)
        {
            m_currentWidgetType = type;
        }

        virtual void Execute(vtkObject* caller, unsigned long eventId, void* callData)
        {
            switch (m_currentWidgetType)
            {
            case CallbackWidgetType::BOX:
            {
                vtkBoxWidget* widget = vtkBoxWidget::SafeDownCast(caller);
                widget->GetPlanes(m_planes);
                break;
            }
            case CallbackWidgetType::PLANE:
            {
                vtkPlaneWidget* pWidget = vtkPlaneWidget::SafeDownCast(caller);
                pWidget->GetPlane(m_singlePlane);
                break;
            }
            default:
                break;
            }
        }

    public:
        CallbackWidgetType m_currentWidgetType;
        vtkPlane* m_singlePlane;
        vtkPlanes* m_planes;
    };



    class WidgetCutter
    {
    public:
        WidgetCutter();

    public:
        enum class WidgetType
        {
            BOX,
            PLANE,
        };

        //平面cutter 平面法向朝向
        enum class PlaneNormalDirection
        {
            X,
            Y,
            Z,
        };

        enum class PlaneRepresentation
        {
            //        OUTLINE,
            WIREFRAME,
            SURFACE,
            POINTS,
        };

        enum class StructureType
        {
            POINTS,
            CELLS
        };

        struct ArrayInformation
        {
            int arrayIndex;
            QString arrayName;
            int arrayComponent;
        };

    public:
        void SetInteractor(vtkRenderWindowInteractor* iren);
        void SetInputData(vtkDataSet* dataSet);
        //获取输入数据的属性表points下标及其对应的数据名称
        std::vector<ArrayInformation> GetPointsArrayIndexAndNames();
        //获取输入数据属性表cells下标及其对应的数据名称
        std::vector<ArrayInformation> GetCellsArrayIndexAndNames();

        bool SetActiveScalarAttribute(int index, const StructureType type,
            const int compIndex = -1);

        //默认使用plane widget
        void SetWidgetType(WidgetType type);
        //plane widget控制方法
        void SetPlaneNormalDirection(PlaneNormalDirection direction);
        vtkProperty* GetWidgetPlaneProperty();
        vtkProperty* GetWidgetHandelProperty();
        vtkPlaneWidget* GetPlaneWidget();
        vtkBoxWidget* GetBoxWidget();

        void SetRepresentationType(PlaneRepresentation type);
        void SetCenter(double x[3]);
        void TurnAllWidgetOff();
        void TurnWidgetOn();

        void Update();
        void refreshWidget();

        vtkCutter* GetCutter();
        vtkPolyDataMapper* GetMapper();
        vtkActor* GetCutterActor();

        //lookupTable
        void SetScalarBarRange(const double min, const double max);
        bool SetScalarBarRangeToAuto();
        void SetLookupTable(vtkLookupTable* lookupTable);
        vtkLookupTable* GetLookupTable();
        //scalarBar
        void SetScalarBarTitle(const std::string& title);
        void SetScalarBarNumberOfLabel(const int num);
        void SetScalarBarDisplayPosition(const int x, const int y);
        vtkScalarBarActor* GetScalarBar();

        //data outline actor
        void SetDataOutlineActorColor(const double r, const double g, const double b);
        vtkActor* GetDataOutlineActor();

        //保存cutter的数据时，只保留指定的场数据
        //useOrigianl为真时，直接保留和原数据对应的所有场，此时后面两个参数不起作用
        //当useOrigianl为假时，使用用户指定的场
        void SelectFieldsToSaveForCutter(bool useOrigianl,
            const std::vector<ArrayInformation>& selectedPointsFiled = std::vector<ArrayInformation>(),
            const std::vector<ArrayInformation>& selectedCellsFiled = std::vector<ArrayInformation>());
        //直接将裁剪数据写入本地
        void WriteTheCutterData(const std::string& fileName);

        //重置widget位置为图像中心
        void ResetCurrentWidgetPositon();

    private:
        void InitPipeLine();
        void InitLookupTable();
        void GenerateDataOutlineBox();

        //提取每个属性的单个分量，最初设计目的是 用于将含有多个成分的属性（如三个组分的数据）设置为标量场属性时，
        //提取其中一个单独的组分作为显示。component为-1时，返回magnitude。
        bool GetSingleComponent(vtkDataArray* array, const int component, vtkDoubleArray* returnArray);

        //获取属性表points单元下标及其对应的数据名称
        std::vector<ArrayInformation> GetPointsArrayIndexAndNames(vtkDataSet* data);
        //获取属性表cells单元下标及其对应的数据名称
        std::vector<ArrayInformation> GetCellsArrayIndexAndNames(vtkDataSet* data);

        void SelectArrayFromCutterData(const std::vector<ArrayInformation>& selectedPointsFiled,
            const std::vector<ArrayInformation>& selectedCellsFiled);

    private:
        //    vtkDataSet* m_inputData{nullptr};

            //输入数据，会随选择的矢量分量更改其内容
        vtkSmartPointer<vtkDataSet> m_inputData;
        //原输入数据备份，用作获取切片数据用
        vtkSmartPointer<vtkDataSet> m_inputDataOriginal;

        vtkSmartPointer<vtkPlaneWidget> m_planeWidget;
        vtkSmartPointer<vtkPlane> m_plane;

        vtkSmartPointer<vtkBoxWidget> m_boxWidget;
        vtkSmartPointer<vtkPlanes> m_planes;

        vtkSmartPointer<vtkCutter> m_cutter;

        vtkSmartPointer<vtkPolyDataMapper> m_selectedDataMapper;
        vtkSmartPointer<vtkActor> m_cutterDataActor;
        vtkSmartPointer<vtkWidgetCall> m_cutterWidgetCall;

        vtkSmartPointer<vtkLookupTable> m_lookupTable;
        vtkSmartPointer<vtkScalarBarWidget> m_scalarBarWidget;
        vtkSmartPointer<vtkScalarBarActor> m_scalarBarActor;

        vtkSmartPointer<vtkActor> m_dataOutlineActor;

        vtkSmartPointer<vtkRenderWindowInteractor> m_interactor;

        vtkSmartPointer<vtkPolyData> m_cutterDataToSave;

        WidgetType m_currentWidgetType;
        int m_currentAddedPointArrayIndex; //设置标量场属性，当多组分array的一个分类被添加后，下次设置时，用于移除。
        int m_currentAddedCellArrayIndex; //设置标量场属性，当多组分array的一个分类被添加后，下次设置时，用于移除。

        bool m_isScalarAttributeSet;//是否设置了用于颜色渲染的标量场
        std::array<double, 2> m_currentArrayRange; //当前设置的标量场数值范围
    };

} //namespace pst

#endif // PST_WIDGETCUTTER_H
